package com.score;

import android.Manifest;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.traaax.music.KeyJni;
import com.traaax.music.Mp3Jni;

import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class PlaybackActivity extends Activity implements Runnable, SeekBar.OnSeekBarChangeListener {
    private SeekBar mSb, mSb2, mSb3;
    private TextView mT1, mT2;
    private Button mReverb;

    private AudioTrack mPlayer;
    private boolean mIsDestroy;
    private SimpleDateFormat sStandardDateFormat = new SimpleDateFormat("mm:ss", Locale.CHINA);
    private boolean mIsTouch;
    private float mSkip, recFlc = 1, musicFlc = 1;
    private long mMinLen;
    private int mReverbType = 0;
    private boolean mIsMix = false;
    private int pitch;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_playback);
        mT1 = findViewById(R.id.t1);
        mT2 = findViewById(R.id.t2);
        mSb = findViewById(R.id.sb);
        mSb2 = findViewById(R.id.sb2);
        mSb3 = findViewById(R.id.sb3);
        mSb.setOnSeekBarChangeListener(this);
        mSb2.setOnSeekBarChangeListener(this);
        mSb3.setOnSeekBarChangeListener(this);
        mReverb = findViewById(R.id.reverb);
        mReverb.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mReverbType++;
                if (mReverbType > 112) {
                    mReverbType = 0;
                }
                Mp3Jni.effectSetReverbType(mReverbType);
                mReverb.setText("混响类型：" + mReverbType);
            }
        });
        pitch = getIntent().getIntExtra("pitch", 0);
        Mp3Jni.effectCreate(44100, 2);
        Mp3Jni.effectSetReverbType(mReverbType);

        int minSize = AudioTrack.getMinBufferSize(44100,
                AudioFormat.CHANNEL_OUT_STEREO,
                AudioFormat.ENCODING_PCM_16BIT);
        mPlayer = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_STEREO,
                AudioFormat.ENCODING_PCM_16BIT, minSize, AudioTrack.MODE_STREAM);
        mPlayer.play();
        mIsDestroy = false;
        new Thread(this).start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mIsDestroy = true;
        mPlayer.stop();
        mPlayer.release();
        Mp3Jni.effectDestroy();
    }

    @Override
    public void run() {
        try {
            FileChannel fis1 = new RandomAccessFile(PlaybackActivity.this.getFilesDir() + "/rec.pcm", "r").getChannel();
            FileChannel fis2 = new RandomAccessFile(PlaybackActivity.this.getFilesDir() + "/music.pcm", "r").getChannel();
            ByteBuffer buff2 = ByteBuffer.allocate(4096);

            long f1len = fis1.size();
            long f2len = fis2.size();
            mMinLen = Math.min(f1len, f2len);
            byte[] b2 = new byte[4096];
            KeyJni keyJni = new KeyJni(44100, 2);
            keyJni.setPitch(pitch);
            while (!mIsDestroy) {
                long playSize = 0;
                fis1.position(0);
                fis2.position(0);
                buff2.clear();
                while (true && !mIsDestroy) {
                    final boolean isTouch = mIsTouch;
                    if (mSkip != 0) {
                        long offset = (long) (mSkip * mMinLen);
                        if (offset % 2 != 0) {
                            offset--;
                        }
                        fis1.position(offset);
                        fis2.position(offset);
                        playSize = offset;
                        mSkip = 0;
                    }
                    int len2 = fis2.read(buff2);
                    if (len2 > 0) {
                        buff2.rewind();
                        buff2.get(b2);
                        buff2.clear();
                        if (pitch != 0) {
                            byte[] o2 = keyJni.process(b2, len2);
                            if (o2 != null) {
                                b2 = o2;
                            }
                        }

                        ByteBuffer buff1 = ByteBuffer.allocate(b2.length);
                        int len1 = fis1.read(buff1);
                        if (len1 != b2.length) {
                            buff1.clear();
                            break;
                        } else {
                            byte[] b1 = new byte[len1];
                            buff1.rewind();
                            buff1.get(b1);
                            buff1.clear();
                            Mp3Jni.effectProcess(b1);
                            byte[] out = Mp3Jni.mix(b1, len1, recFlc, b2, len2, musicFlc);
                            int len = mPlayer.write(out, 0, out.length);
                            playSize += len;
                            final float progress = 1000 * playSize * 1f / mMinLen;
                            final long pz = playSize;
                            if (!isTouch) {
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        if (!isTouch) {
                                            mSb.setProgress((int) progress);
                                            mT1.setText(sStandardDateFormat.format(new Date((long) (pz / 88.2 / 2))));
                                            mT2.setText(sStandardDateFormat.format(new Date((long) (mMinLen / 88.2 / 2))));
                                        }
                                    }
                                });
                            }
                        }
                    } else {
                        break;
                    }
                }
            }
            fis1.close();
            fis2.close();
            keyJni.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
        if (seekBar == mSb) {
            if (b) {
                mT1.setText(sStandardDateFormat.format(new Date((long) (mMinLen * 1f * i / 1000 / 88.2 / 2))));
            }
        } else if (seekBar == mSb2) {
            recFlc = 1f * i / 500;
        } else if (seekBar == mSb3) {
            musicFlc = 1f * i / 500;
        }
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        if (seekBar == mSb) {
            mIsTouch = true;
        }
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        if (seekBar == mSb) {
            mSkip = 1f * seekBar.getProgress() / 1000;
            mIsTouch = false;
        }
    }

    public void mix(View v) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                String[] perms = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
                ActivityCompat.requestPermissions(this, perms, 10);
                return;
            }
        }
        if (mIsMix) {
            return;
        }
        mIsDestroy = true;
        mIsMix = true;
        final ProgressDialog dialog = ProgressDialog.show(PlaybackActivity.this, "合成中", "进度：0", false, false);
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    FileChannel fis1 = new RandomAccessFile(PlaybackActivity.this.getFilesDir() + "/rec.pcm", "r").getChannel();
                    FileChannel fis2 = new RandomAccessFile(PlaybackActivity.this.getFilesDir() + "/music.pcm", "r").getChannel();
                    ByteBuffer buff2 = ByteBuffer.allocate(4096);
                    FileOutputStream fos = new FileOutputStream("/mnt/sdcard/mix.mp3");
                    long f1len = fis1.size();
                    long f2len = fis2.size();
                    mMinLen = Math.min(f1len, f2len);
                    byte[] b2 = new byte[4096];
                    long playSize = 0, preSize = 0;
                    Mp3Jni.createEncoder(7, 44100, 44100, 2);
                    KeyJni keyJni = new KeyJni(44100, 2);
                    keyJni.setPitch(pitch);
                    while (true) {
                        int len2 = fis2.read(buff2);
                        if (len2 > 0) {
                            buff2.rewind();
                            buff2.get(b2);
                            buff2.clear();
                            if (pitch != 0) {
                                byte[] o2 = keyJni.process(b2, len2);
                                if (o2 != null) {
                                    b2 = o2;
                                }
                            }

                            ByteBuffer buff1 = ByteBuffer.allocate(b2.length);
                            int len1 = fis1.read(buff1);
                            if (len1 != b2.length) {
                                buff1.clear();
                                break;
                            } else {
                                byte[] b1 = new byte[len1];
                                buff1.rewind();
                                buff1.get(b1);
                                buff1.clear();
                                Mp3Jni.effectProcess(b1);
                                byte[] out = Mp3Jni.mix(b1, len1, recFlc, b2, len2, musicFlc);
                                playSize += out.length;
                                out = Mp3Jni.encode2mp3(out, out.length);
                                fos.write(out);
                                final long progress = 100 * playSize / mMinLen;
                                if (progress != preSize) {
                                    preSize = progress;
                                    runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            dialog.setMessage("进度：" + progress);
                                        }
                                    });
                                }
                            }
                        } else {
                            break;
                        }
                    }
                    keyJni.close();
                    Mp3Jni.closeEncoder();
                    dialog.dismiss();
                    fos.close();
                    fis1.close();
                    fis2.close();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(PlaybackActivity.this, "合成成功，保存在/sdcard/mix.mp3", Toast.LENGTH_LONG).show();
                            finish();
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == 10) {
            if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "请给与写入sd卡权限", Toast.LENGTH_LONG).show();
            }
        }
    }
}
